package com.kitten.user.runner;

import cn.hutool.core.collection.CollUtil;
import com.google.common.collect.Maps;

import com.kitten.framework.common.utils.JsonUtils;
import com.kitten.user.constants.RedisKeyConstants;
import com.kitten.user.domain.dataobject.PermissionDO;
import com.kitten.user.domain.dataobject.RoleDO;
import com.kitten.user.domain.dataobject.RolePermissionDO;
import com.kitten.user.domain.mapper.PermissionDOMapper;
import com.kitten.user.domain.mapper.RoleDOMapper;
import com.kitten.user.domain.mapper.RolePermissionDOMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.assertj.core.util.Lists;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
@Slf4j
public class PushRolePermissions2RedisRunner implements ApplicationRunner {
    @Resource
    private RedisTemplate<String, String> redisTemplate;
    @Resource
    private RoleDOMapper roleDOMapper;
    @Resource
    private PermissionDOMapper permissionDOMapper;
    @Resource
    private RolePermissionDOMapper rolePermissionDOMapper;


    // 权限同步标记 Key
    private static final String PUSH_PERMISSION_FLAG = "push.permission.flag";

    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("==> 服务启动，开始同步角色权限数据到 Redis 中...");

        // todo
        try {
            Boolean canPushed = redisTemplate.opsForValue().setIfAbsent(PUSH_PERMISSION_FLAG, "1", 1, TimeUnit.DAYS);
            if (!canPushed) {
                log.info("==> 权限同步标记已存在，跳过权限同步...");
                return;
            }
            // 1. 查询所有角色
            List<RoleDO> roleDOS = roleDOMapper.selectAllEnabled();
            if (CollUtil.isNotEmpty(roleDOS)) {
                List<Long> roleIds = roleDOS.stream().map(RoleDO::getId).toList();  // 角色id列表 [1]
                List<RolePermissionDO> rolePermissionDOS = rolePermissionDOMapper.selectByRoleIds(roleIds);     // 角色权限列表
                Map<Long, List<Long>> roleIdPermissionIdsMap = rolePermissionDOS.stream().collect(Collectors.groupingBy(RolePermissionDO::getRoleId, Collectors.mapping(RolePermissionDO::getPermissionId, Collectors.toList())));

                List<PermissionDO> permissionDOS = permissionDOMapper.selectAppEnabledList();   // 查询APP端所有被启用的权限, 就是permission表
                // 权限ID - 权限DO
                Map<Long, PermissionDO> permissionIdDOMap = permissionDOS.stream().collect(Collectors.toMap(PermissionDO::getId, permissionDO -> permissionDO));

                // 2. 遍历角色权限Map，将角色权限数据写入Redis
                // 角色 - 权限 Map
                Map<String, List<String>> roleKeyPermissionsMap = Maps.newHashMap();
                roleDOS.forEach(roleDO -> {
                    Long roleId = roleDO.getId();       // 角色ID
                    String roleKey = roleDO.getRoleKey();    // 角色Key(角色标识common_user)
                    List<Long> permissionIds = roleIdPermissionIdsMap.get(roleId);
                    if (CollUtil.isNotEmpty(permissionIds)) {    // 角色有权限
                        List<String> permissionKeys = Lists.newArrayList();
                        permissionIds.forEach(permissionId -> {
                            PermissionDO permissionDO = permissionIdDOMap.get(permissionId);
                            permissionKeys.add(permissionDO.getPermissionKey());
                        });
                        roleKeyPermissionsMap.put(roleKey, permissionKeys);
                    }
                });
                log.info("==> {} ", roleKeyPermissionsMap.toString());
                // 同步至 Redis 中，方便后续网关查询鉴权使用
                roleKeyPermissionsMap.forEach((roleKey, permissionKey) -> {
                    String key = RedisKeyConstants.buildRolePermissionsKey(roleKey);     // 角色权限Key = role:permissions:roleId
                    redisTemplate.opsForValue().set(key, JsonUtils.toJsonString(permissionKey));
                });
            }
            log.info("==> 服务启动，同步角色权限数据到 Redis 中成功...");
        } catch (Exception e) {
            log.info("==> 服务启动，同步角色权限数据到 Redis 中失败...");
        }
    }
}
